package com.towerdefence
{

import com.etc.CurrentCell_mc;
import com.etc.DebugCell_mc;
import com.towerdefence.enemies.EnemySoldier;

import flash.display.Sprite;
	import flash.display.MovieClip;
	import flash.display.BitmapData;
	import flash.display.Bitmap;
	import flash.geom.*;
	import flash.events.Event;
	import flash.events.MouseEvent;
	import flash.utils.getTimer;
	
	import com.framework.math.*;
	import com.framework.SimpleCache;
	
	import com.towerdefence.enemies.*;
	import com.towerdefence.editor.Brush;
	import com.towerdefence.towers.*;
	import com.towerdefence.controllers.ObjectController;
	
	public class Universe extends Sprite
	{
		
		//---------------------------------------
		// CLASS CONSTANTS
		//---------------------------------------
		
		// Максимальный размер карты по ширине и высоте в клетках
		public static const MAP_WIDTH_MAX:int = 20;
		public static const MAP_HEIGHT_MAX:int = 15;
		
		// Размер ячейки в пикселях
		public static const MAP_CELL_SIZE:int = 32;
		public static const MAP_CELL_HALF:int = 16;
		
		// Состояние ячеек карты проходимости
		public static const STATE_CELL_FREE:int = 1;
		public static const STATE_CELL_BUSY:int = 2;
		public static const STATE_CELL_BUILD_ONLY:int = 3;
		public static const STATE_CELL_START:int = 4;
		public static const STATE_CELL_FINISH:int = 5;
		
		//---------------------------------------
		// PUBLIC VARIABLES
		//---------------------------------------
		public var mousePosX:int = 0;
		public var mousePosY:int = 0;
		
		public var cellPosX:int = 0;
		public var cellPosY:int = 0;
		
		public var towers:ObjectController;
		public var enemies:ObjectController;
		public var bullets:ObjectController;
		
		public var cacheEnemySoldier:SimpleCache;
		public var cacheGunTower:SimpleCache;
		public var cacheGunBullet:SimpleCache;
		
		//---------------------------------------
		// PRIVATE VARIABLES
		//---------------------------------------
		private static var _instance:Universe;
		
		private var _mapMask:Array;
		private var _debugGrid:Bitmap;
		private var _currentCell:Sprite;
		
		private var _startPoints:Array = [];
		private var _finishPoints:Array = [];
		
		private var _deltaTime:Number = 0; // Текущее delta время
		private var _lastTick:int = 0; // Последний тик таймера (для расчета нового delta времени)
		private var _maxDeltaTime:Number = 0.03;
		
		private var _isEditor:Boolean = false;

		private var _enemyFactory: EnemyFactory;
		
		//---------------------------------------
		// CONSTRUCTOR
		//---------------------------------------
		
		/**
		 * @constructor
		 */
		public function Universe()
		{
			// Класс мира создается в единственном экземпляре и 
			// ссылка на него хранится в приватной статической переменной.
			// При попытки создать второй экземпляр мира сообщаем об ошибке.
			if (_instance != null)
			{
				throw("Error: Мир уже существует. Используйте Universe.getInstance();");
			}
			
			// Ссылка на экземпляр мира
			_instance = this;
			
			_debugGrid = new Bitmap();
			addChild(_debugGrid);
			
//			_currentCell = new CurrentCell_mc();
			_currentCell = new CurrentCell_mc();
			addChild(_currentCell);
			
			towers = new ObjectController();
			enemies = new ObjectController();
			bullets = new ObjectController();
			
			cacheEnemySoldier = new SimpleCache(EnemySoldier, 50);
			cacheGunTower = new SimpleCache(GunTower, 20);
			cacheGunBullet = new SimpleCache(GunBullet, 50);


			/*var tower:GunTower = new GunTower();
			tower.init(2, 1);
			
			tower = new GunTower();
			tower.init(2, 3);*/
			
			// Добавляем основной обработчик мира
			addEventListener(Event.ENTER_FRAME, enterFrameHandler);
		}

		//---------------------------------------
		// PRIVATE METHODS
		//---------------------------------------
		
		/**
		 * Создает отладочную сетку карты проходимости.
		 */
		private function updateDebugGrid():void
		{
		  // Графический образ ячейки
//		  var cellSprite:MovieClip = new DebugCell_mc();
		  var cellSprite:MovieClip = new DebugCell_mc();

		  // Растровый холст для рисования сетки
		  var bmpData:BitmapData = new BitmapData(MAP_CELL_SIZE * MAP_WIDTH_MAX, 
		    MAP_CELL_SIZE * MAP_HEIGHT_MAX, true, 0x00000000);

		  // Начальное положение текущей ячейки по высоте/ширине
		  var matrix:Matrix = new Matrix();
		  matrix.tx = MAP_CELL_HALF; 
		  matrix.ty = MAP_CELL_HALF;

		  // Двигаемся по высоте карты
		  for (var ay:int = 0; ay < MAP_HEIGHT_MAX; ay++)
		  {
		    // Двигаемся по ширине карты
		    for (var ax:int = 0; ax < MAP_WIDTH_MAX; ax++)
		    {
		      // Переключаем состояние ячейки
		      cellSprite.gotoAndStop(getCellState(ax, ay));

		      // Рисуем текущую ячейку
		      bmpData.draw(cellSprite, matrix);

		      // Меняем положение текущей ячейки по ширине
		      matrix.tx += MAP_CELL_SIZE;
		    }

		    // Меняем положение текущей ячейки по высоте
		    matrix.ty += MAP_CELL_SIZE;
		    // Обнуляем ширину
		    matrix.tx = MAP_CELL_HALF;
		  }

		  // Передаем растровый холст в картинку
		  _debugGrid.bitmapData = bmpData;

		  cellSprite = null;
		}
		
		/**
		 * Создает/очишает карту проходимости.
		 */
		private function clearMapMask():void
		{
			// Создаем новый массив
			_mapMask = [];
			_mapMask.length = MAP_HEIGHT_MAX;
			
			// Двигаемся по высоте карты
			for (var ay:int = 0; ay < MAP_HEIGHT_MAX; ay++)
			{
				// Добавляем новую строку в массив
				_mapMask[ay] = [];
				_mapMask[ay].length = MAP_WIDTH_MAX;
				
				// Двигаемся по ширине карты
				for (var ax:int = 0; ax < MAP_WIDTH_MAX; ax++)
				{
					// Задаем ячеке свободное состояние
					_mapMask[ay][ax] = STATE_CELL_FREE;
				}
			}
		}
		
		//---------------------------------------
		// PUBLIC METHODS
		//---------------------------------------
		
		/**
		 * Обновляет координаты мышки.
		 */
		public function updateMousePos(mouseX:int, mouseY:int):void
		{
		  mousePosX = mouseX;
		  mousePosY = mouseY;
		
		  // Координаты тайла, над которым находится курсор мыши
		  cellPosX = int(mouseX / MAP_CELL_SIZE);
		  cellPosY = int(mouseY / MAP_CELL_SIZE);
		
		  // Подсветка текущего тайла
		  _currentCell.x = MAP_CELL_HALF + cellPosX * MAP_CELL_SIZE;
		  _currentCell.y = MAP_CELL_HALF + cellPosY * MAP_CELL_SIZE;
		}
		
		/**
		 * Возвращает состояние указанной 
		 * клетки в карте проходимости.
		 */
		public function getCellState(ax:int, ay:int):int
		{
			// Проверка на выход за приделы массива
			if (ax >= 0 && ax < MAP_WIDTH_MAX &&
				ay >= 0 && ay < MAP_HEIGHT_MAX)
			{
				return _mapMask[ay][ax];
			}
			else
			{
				return STATE_CELL_BUSY;
			}
		}
		
		/**
		 * Устанавливает состояние указанной 
		 * клетке в карте проходимости.
		 */
		public function setCellState(ax:int, ay:int, state:int = STATE_CELL_FREE):void
		{
			// Проверка на выход за приделы массива
			if (ax >= 0 && ax < MAP_WIDTH_MAX &&
				ay >= 0 && ay < MAP_HEIGHT_MAX)
			{
				_mapMask[ay][ax] = state;
			}
		}
		
		/**
		 * Создает нового врага (debug). 
		 */
		public function newEnemy():void
		{
			// Создание тестового врага
//			var soldier:EnemySoldier = cacheEnemySoldier.get() as EnemySoldier;
			var r: Number = Amath.random(0,2);
			var soldier:EnemySoldier = null;

			if( r == 0 ){
				soldier = EnemyFactory.newEnemy("enemy-a");
			} else if( r == 1 ){
				soldier = EnemyFactory.newEnemy("enemy-b");
			} else if ( r == 2 ){
				soldier = EnemyFactory.newEnemy("enemy-c");
			}

			// Выбираем случайную стартовую точку и финишную
			var startPos:Avector = _startPoints[Amath.random(1, _startPoints.length) - 1];
			var finishPos:Avector = _finishPoints[Amath.random(1, _finishPoints.length) - 1];
			
			// Инициализируем юнита с выбранными точками
			soldier.init(startPos.x, startPos.y, finishPos.x, finishPos.y);
		}
		
		/**
		 * Переводит значение из пикселей в тайлы.
		 */
		public static function toTile(value:Number):int
		{
			return int(value / MAP_CELL_SIZE);
		}
		
		/**
		 * Переводит значение из тайлов в пиксели.
		 */
		public static function toPix(value:int):Number
		{
			return MAP_CELL_HALF + value * MAP_CELL_SIZE;
		}
		
		/**
		 * Применяет свойства кисточки к карте.
		 */
		public function applyBrush(brush:Brush):void
		{
			// Рисуем только если изменилось положение кисти
			if (brush.tileX != cellPosX || brush.tileY != cellPosY)
			{
				// Если ячейка занята то освобождаем её
				var cellState:int = getCellState(cellPosX, cellPosY);
				if (cellState != STATE_CELL_FREE && cellState == brush.kind)
				{
					setCellState(cellPosX, cellPosY, STATE_CELL_FREE);
				}
				else
				{
					setCellState(cellPosX, cellPosY, brush.kind);
				}
				
				brush.tileX = cellPosX;
				brush.tileY = cellPosY;
				updateDebugGrid();
			}
		}
		
		/**
		 * @private
		 */
		public function buildTower():void
		{
			if (getCellState(cellPosX, cellPosY) != STATE_CELL_BUSY)
			{
				var rnd: Number = Amath.random(0,3), name: String = 'tower-a';
				if( rnd == 0 ){
					name = 'tower-a';
				} else if( rnd == 1 ){
					name = 'tower-b';
				} else if( rnd == 2 ){
					name = 'tower-c';
				}
				var tower:GunTower = TowerFactory.newTower(name);
				tower.init(cellPosX, cellPosY);
				setCellState(cellPosX, cellPosY, STATE_CELL_BUSY);
				updateDebugGrid();
			}
		}
		
		//---------------------------------------
		// PRIVATE METHODS
		//---------------------------------------
		
		/**
		 * Создает список стартовых и финишных 
		 * точек на основе карты проходимости.
		 */
		public function preparePoints():void
		{
			// Очищаем списки
			_startPoints.length = 0;
			_finishPoints.length = 0;
			
			// Перебераем карту проходимости и создаем новые точки
			for (var ty:int = 0; ty < _mapMask.length; ty++)
			{
				for (var tx:int = 0; tx < _mapMask[0].length; tx++)
				{
					switch (_mapMask[ty][tx])
					{
						// Стартовая точка
						case STATE_CELL_START :
							_startPoints.push(new Avector(tx, ty));
							if (!_isEditor)
							{
								_mapMask[ty][tx] = STATE_CELL_FREE;
							}
						break;
						
						// Финишная точка
						case STATE_CELL_FINISH :
							_finishPoints.push(new Avector(tx, ty));
							if (!_isEditor)
							{
								_mapMask[ty][tx] = STATE_CELL_FREE;
							}
						break;
					}
				}
			}
			
		}
				
		//---------------------------------------
		// EVENT HANDLERS
		//---------------------------------------
		
		/**
		 * Обработка всех динамических объектов.
		 */
		private function enterFrameHandler(event:Event):void
		{
			// Рассчет delta времени
			_deltaTime = (getTimer() - _lastTick) / 1000;
			_deltaTime = (_deltaTime > _maxDeltaTime) ? _maxDeltaTime : _deltaTime;
			
			enemies.update(_deltaTime);
			towers.update(_deltaTime);
			bullets.update(_deltaTime);
			
			_lastTick = getTimer();
		}
		
		//---------------------------------------
		// GETTER / SETTERS
		//---------------------------------------
		
		/**
		 * Возвращает ссылку на экземпляр мира,
		 * доступно в любом классе приложения.
		 */
		public static function getInstance():Universe
		{
			return (_instance == null) ? new Universe() : _instance;
		}
		
		/**
		 * Устанавливает режим редактора.
		 */
		public function editorMode(value:Boolean):void
		{
			_isEditor = value;
		}
		
		/**
		 * Возвращает карту проходимости.
		 */
		public function get mapMask():Array
		{
			return _mapMask;
		}
		
		/**
		 * Устанавливает карту проходимости.
		 */
		public function set mapMask(value:Array):void
		{
			_mapMask = value;
			preparePoints();
			updateDebugGrid();
		}
		
	}

}